namespace Xmef.Serializer
{
    using System;
    using System.Xml.Serialization;
    using Xmef.Interfaces;
    using Xmef.Library;
    using Xmef.Serializer.Internal;

    [Serializable]
    public abstract class XmlTypeSerializer<T> : XmlTypeSettings, IXmlRuntimeFormatter, IXmlTypeFormatter
    {
        [NonSerialized]
        private XmlFormatAttributes<T> _attributes;
        [NonSerialized]
        private XmlSerializer _serializer;
        [NonSerialized]
        private readonly bool _useOnDemandRuntimeSerialization = true;

        public XmlTypeSerializer() : base(typeof(T)) { }

        protected XmlTypeSerializer(bool useOnDemandRuntimeSerialization) : base(typeof(T))
        {
            this._useOnDemandRuntimeSerialization = useOnDemandRuntimeSerialization;
            if (!this._useOnDemandRuntimeSerialization)
            {
                this.AddOverrides();
            }
        }

        public void AddArray(IXmlTypeFormatter formatter, string member, string attributeName)
        {
            this.AddOverrides(formatter, delegate (XmlAttributes attributes) {
                attributes.XmlArray = new XmlArrayAttribute(attributeName);
            }, member);
        }

        public void AddArray(IXmlTypeFormatter formatter, string member, XmlArrayAttribute xmlArray)
        {
            this.AddOverrides(formatter, delegate (XmlAttributes attributes) {
                attributes.XmlArray = xmlArray;
            }, member);
        }

        public void AddArrayItem(IXmlTypeFormatter formatter, string member, XmlArrayItemAttribute xmlArrayItem)
        {
            this.AddOverrides(formatter, delegate (XmlAttributes attributes) {
                attributes.XmlArrayItems.Add(xmlArrayItem);
            }, member);
        }

        public void AddArrayItem(IXmlTypeFormatter formatter, string member, string attributeName, Type type)
        {
            this.AddOverrides(formatter, delegate (XmlAttributes attributes) {
                attributes.XmlArrayItems.Add(new XmlArrayItemAttribute(attributeName, type));
            }, member);
        }

        public void AddAttribute(IXmlTypeFormatter formatter, string member, string attributeName)
        {
            this.AddOverrides(formatter, delegate (XmlAttributes attributes) {
                attributes.XmlAttribute = new XmlAttributeAttribute(attributeName);
            }, member);
        }

        public void AddAttribute(IXmlTypeFormatter formatter, string member, XmlAttributeAttribute xmlAttribute)
        {
            this.AddOverrides(formatter, delegate (XmlAttributes attributes) {
                attributes.XmlAttribute = xmlAttribute;
            }, member);
        }

        public void AddAttribute(IXmlTypeFormatter formatter, string member, string attributeName, Type type)
        {
            this.AddOverrides(formatter, delegate (XmlAttributes attributes) {
                attributes.XmlAttribute = new XmlAttributeAttribute(attributeName, type);
            }, member);
        }

        private static void AddAttributes(IXmlTypeFormatterInternal formatter, Type type, Action<XmlAttributes> func)
        {
            XmlTypeSerializer<T>.AddType(formatter.InternalSerializer, type, func);
        }

        private static void AddAttributes(IXmlTypeFormatterInternal formatter, Type type, string member, Action<XmlAttributes> func)
        {
            XmlTypeSerializer<T>.AddType(formatter.InternalSerializer, type, member, func);
        }

        public void AddElement(IXmlTypeFormatter formatter, string member, string attributeName)
        {
            this.AddOverrides(formatter, delegate (XmlAttributes attributes) {
                attributes.XmlElements.Add(new XmlElementAttribute(attributeName));
            }, member);
        }

        public void AddElement(IXmlTypeFormatter formatter, string member, XmlElementAttribute xmlElement)
        {
            this.AddOverrides(formatter, delegate (XmlAttributes attributes) {
                attributes.XmlElements.Add(xmlElement);
            }, member);
        }

        public void AddElement(IXmlTypeFormatter formatter, string member, string attributeName, Type type)
        {
            this.AddOverrides(formatter, delegate (XmlAttributes attributes) {
                attributes.XmlElements.Add(new XmlElementAttribute(attributeName, type));
            }, member);
        }

        public void AddEnum(IXmlTypeFormatter formatter, string member, string attributeName)
        {
            this.AddOverrides(formatter, delegate (XmlAttributes attributes) {
                attributes.XmlEnum = new XmlEnumAttribute(attributeName);
            }, member);
        }

        public void AddEnum(IXmlTypeFormatter formatter, string member, XmlEnumAttribute xmlEnum)
        {
            this.AddOverrides(formatter, delegate (XmlAttributes attributes) {
                attributes.XmlEnum = xmlEnum;
            }, member);
        }

        public void AddIgnore(IXmlTypeFormatter formatter, string member)
        {
            this.AddOverrides(formatter, delegate (XmlAttributes attributes) {
                attributes.XmlIgnore = true;
            }, member);
        }

        public void AddNamespace(IXmlTypeFormatter formatter, bool ns)
        {
            this.AddOverrides(formatter, delegate (XmlAttributes attributes) {
                attributes.Xmlns = ns;
            });
        }

        private void AddOverrides()
        {
            this._attributes = new XmlFormatAttributes<T>();
            try
            {
                this.AddOverrides(this);
                if (this._attributes != null)
                {
                    this._serializer = this._attributes.RuntimeSerializer;
                }
            }
            catch (ApplicationException ex)
            {
                LogHelper.WriteErrorSync(ex);
                IXmlRuntimeSerializerInternal instance = this;
                if (instance != null)
                {
                    this._serializer = new XmlSerializer(instance.RuntimeType);
                }
            }
        }

        public abstract void AddOverrides(IXmlTypeFormatter formatter);
        private void AddOverrides(IXmlTypeFormatter formatter, Action<XmlAttributes> func)
        {
            try
            {
                IXmlRuntimeSerializerInternal serializer = Runtime.GetSerializer(formatter);
                if (serializer != null)
                {
                    IXmlRuntimeSerializerInternal instance = this;
                    if (instance != null)
                    {
                        XmlTypeSerializer<T>.AddType(serializer, instance.RuntimeType, func);
                    }
                }
            }
            catch (ApplicationException ex)
            {
                LogHelper.WriteErrorSync(ex);
            }
        }

        private void AddOverrides(IXmlTypeFormatter formatter, Action<XmlAttributes> func, string member)
        {
            try
            {
                IXmlRuntimeSerializerInternal serializer = Runtime.GetSerializer(formatter);
                if (serializer != null)
                {
                    IXmlRuntimeSerializerInternal instance = this;
                    if (instance != null)
                    {
                        XmlTypeSerializer<T>.AddType(serializer, instance.RuntimeType, member, func);
                    }
                }
            }
            catch (ApplicationException ex)
            {
                LogHelper.WriteErrorSync(ex);
            }
        }

        public void AddOverrides(IXmlTypeFormatter formatter, Type type, Action<XmlAttributes> func)
        {
            IXmlTypeFormatterInternal _formatter = formatter as IXmlTypeFormatterInternal;
            XmlTypeSerializer<T>.AddAttributes(_formatter, type, func);
        }

        public void AddOverrides(IXmlTypeFormatter formatter, Type type, string member, Action<XmlAttributes> func)
        {
            IXmlTypeFormatterInternal _formatter = formatter as IXmlTypeFormatterInternal;
            XmlTypeSerializer<T>.AddAttributes(_formatter, type, member, func);
        }

        public void AddRoot(IXmlTypeFormatter formatter, string attributeName)
        {
            this.AddOverrides(formatter, delegate (XmlAttributes attributes) {
                attributes.XmlRoot = new XmlRootAttribute(attributeName);
            });
        }

        public void AddRoot(IXmlTypeFormatter formatter, XmlRootAttribute xmlRoot)
        {
            this.AddOverrides(formatter, delegate (XmlAttributes attributes) {
                attributes.XmlRoot = xmlRoot;
            });
        }

        public void AddText(IXmlTypeFormatter formatter, string member)
        {
            this.AddOverrides(formatter, delegate (XmlAttributes attributes) {
                attributes.XmlText = new XmlTextAttribute();
            }, member);
        }

        public void AddText(IXmlTypeFormatter formatter, string member, Type type)
        {
            this.AddOverrides(formatter, delegate (XmlAttributes attributes) {
                attributes.XmlText = new XmlTextAttribute(type);
            }, member);
        }

        public void AddText(IXmlTypeFormatter formatter, string member, XmlTextAttribute xmlText)
        {
            this.AddOverrides(formatter, delegate (XmlAttributes attributes) {
                attributes.XmlText = xmlText;
            }, member);
        }

        public void AddType(IXmlTypeFormatter formatter, string attributeName)
        {
            this.AddOverrides(formatter, delegate (XmlAttributes attributes) {
                attributes.XmlType = new XmlTypeAttribute(attributeName);
            });
        }

        public void AddType(IXmlTypeFormatter formatter, XmlTypeAttribute xmlType)
        {
            this.AddOverrides(formatter, delegate (XmlAttributes attributes) {
                attributes.XmlType = xmlType;
            });
        }

        private static void AddType(IXmlRuntimeSerializerInternal serializer, Type type, Action<XmlAttributes> func)
        {
            XmlAttributes attributes = new XmlAttributes();
            IXmlFormatAttributesInternal format = Runtime.GetFormatAttributes(serializer);
            func(attributes);
            format.Add(type, attributes);
        }

        private static void AddType(IXmlRuntimeSerializerInternal serializer, Type type, string member, Action<XmlAttributes> func)
        {
            XmlAttributes attributes = new XmlAttributes();
            IXmlFormatAttributesInternal format = Runtime.GetFormatAttributes(serializer);
            func(attributes);
            format.Add(type, member, attributes);
        }

        internal override IXmlFormatAttributesInternal InternalAttributes
        {
            get
            {
                return this._attributes;
            }
        }

        internal override XmlSerializer InternalSerializer
        {
            get
            {
                if (this._useOnDemandRuntimeSerialization)
                {
                    this.AddOverrides();
                }
                return this._serializer;
            }
        }

        public bool IsOnDemand
        {
            get
            {
                return this._useOnDemandRuntimeSerialization;
            }
        }
    }
}

